package concurrent;

import org.openjdk.jcstress.JCStress;
import org.openjdk.jcstress.Options;
import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.I_Result;

/**
 * jcstress 是 java 并发压测工具：https://github.com/openjdk/jcstress
 *
 * @see org.openjdk.jcstress.Main，参数 -t 指定测试包名，不指定的话是默认扫描项目所有包。
 * 在执行完成后，会在项目所在的目录下生成 results 文件夹，里面有个 index.html，用浏览器打开即可查看报告。
 * 或者 package(需要 maven-shade-plugin) 后直接 java -jar jcstress.jar，也可以获得测试结果
 * <p>
 * I_Result 是一个对象，有一个属性 r1 用来保存结果，在多线程情况下可能出现几种结果？
 * 情况 1：线程 1 先执行 actor1，这时 ready = false，所以进入 else 分支结果为 1。
 * 情况 2：线程 2 执行到 actor2，执行了 num = 2; 和 ready = true，线程 1 执行，这回进入 if 分支，结果为 4。
 * 情况 3：线程 2 先执行 actor2，只执行 num = 2; 但没来得及执行 ready = true，线程 1 执行，还是进入 else 分支，结果为 1。
 */
@State // 标注需要测试的类
@JCStressTest // 指定使用并发测试
// 表示有可能会出现的结果，其中 0 是我们感兴趣的结果
@Outcome(id = {"1", "4"}, expect = Expect.ACCEPTABLE, desc = "ok") // 预测的结果与类型，附加描述信息
@Outcome(id = "0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "danger") // 预测的结果与类型，附加描述信息
public class T01_Order extends org.openjdk.jcstress.Main {
    int num = 0;
    boolean ready = false;

    @Actor // 标记方法使用多线程
    public void actor1(I_Result r) { // 线程 1 执行的代码
        if (ready) {
            r.r1 = num + num;
        } else {
            r.r1 = 1;
        }
    }

    @Actor // 标记方法使用多线程
    public void actor2(I_Result r) { // 线程 2 执行的代码
        num = 2;
        ready = true;
    }

    public static void main(String[] args) throws Exception {
        args = new String[]{
                "-t", T01_Order.class.getPackage().getName(),
                // "-p", "JavaSE/target/jcstress-results.bin.gz"
        };
        System.out.println("Java Concurrency Stress Tests");
        System.out.println("---------------------------------------------------------------------------------");

        Options opts = new Options(args);
        if (!opts.parse()) {
            System.exit(1);
        }

        JCStress jcstress = new JCStress(opts);
        if (opts.shouldList()) {
            for (String test : jcstress.getTests()) {
                System.out.println(test);
            }
        } else if (opts.shouldParse()) {
            jcstress.parseResults();
        } else {
            jcstress.run();
        }
    }
}
